//
//  LcbNetworking.m
//  LcbNetworking
//
//  Created by yiban on 2017/1/22.
//  Copyright © 2017年 liuchengbin. All rights reserved.
//

#import "LcbNetworking.h"
#import "AFNetworking.h"
#import "AFNetworkActivityIndicatorManager.h"
#import "LcbNetworking+requestManager.h"
#import "XDCacheManager.h"
#define Lcb_ERROR_IMFORMATION @"网络出现错误，请检查网络连接"
#define Lcb_ERROR_URLIMFORMATION @"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL"

#define Lcb_NET_ERROR [NSError errorWithDomain:@"www.lcb123.cn" code:-999 userInfo:@{ NSLocalizedDescriptionKey:Lcb_ERROR_IMFORMATION}]
#define Lcb_URL_ERROR [NSError errorWithDomain:@"www.lcb123.cn" code:-999 userInfo:@{ NSLocalizedDescriptionKey:Lcb_ERROR_URLIMFORMATION}]

static BOOL sg_isEnableInterfaceDebug = NO;
static NSMutableArray   *requestTasksPool;

static NSDictionary     *headers;

static NetworkStatus  networkStatus;

static NSTimeInterval   requestTimeout = 20.f;

@implementation LcbNetworking

#pragma mark - manager
+ (AFHTTPSessionManager *)manager {
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    //默认解析模式
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    
    //配置请求序列化
    AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
    
    [serializer setRemovesKeysWithNullValues:YES];
    
    manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
    
    manager.requestSerializer.timeoutInterval = requestTimeout;
    // 设置允许同时最大并发数量，过大容易出问题
    manager.operationQueue.maxConcurrentOperationCount = 5;
    for (NSString *key in headers.allKeys) {
        if (headers[key] != nil) {
            [manager.requestSerializer setValue:headers[key] forHTTPHeaderField:key];
        }
    }
    
    //配置响应序列化
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
                                                                              @"text/html",
                                                                              @"text/json",
                                                                              @"text/plain",
                                                                              @"text/javascript",
                                                                              @"text/xml",
                                                                              @"image/*",
                                                                              @"application/octet-stream",
                                                                              @"application/zip"]];
    
    
    [self checkNetworkStatus];
    
    //每次网络请求的时候，检查此时磁盘中的缓存大小，阈值默认是40MB，如果超过阈值，则清理LRU缓存,同时也会清理过期缓存，缓存默认SSL是7天，磁盘缓存的大小和SSL的设置可以通过该方法[LcbCacheManager shareManager] setCacheTime: diskCapacity:]设置
    [[XDCacheManager shareManager] clearLRUCache];
    
    return manager;
}

#pragma mark - 检查网络
+ (void)checkNetworkStatus {
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    
    [manager startMonitoring];
    
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
       
        switch (status) {
            case AFNetworkReachabilityStatusNotReachable:
                networkStatus = NetworkStatusNotReachable;
                break;
            case AFNetworkReachabilityStatusUnknown:
                networkStatus = NetworkStatusUnknown;
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                networkStatus = NetworkStatusReachableViaWWAN;
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                networkStatus = NetworkStatusReachableViaWiFi;
                break;
            default:
                networkStatus = NetworkStatusUnknown;
                break;
        }
        
    }];
}

+ (NSMutableArray *)allTasks {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (requestTasksPool == nil) requestTasksPool = [NSMutableArray array];
    });
    
    return requestTasksPool;
}

+ (void)enableInterfaceDebug:(BOOL)isDebug {
    sg_isEnableInterfaceDebug = isDebug;
}

+ (BOOL)isDebug {
    return sg_isEnableInterfaceDebug;
}

/**
 *  请求
 *
 *  @param url              请求路径
 *  @param method           请求方式
 *  @param cache            是否缓存
 *  @param refresh          解释同上
 *  @param params           拼接参数
 *  @param progressBlock    进度回调
 *  @param successBlock     成功回调
 *  @param failBlock        失败回调
 *
 *  @return 返回的对象中可取消请求
 */
+ (LcbURLSessionTask *)requestWithUrl:(NSString *)url
                              method:(NRequestMethod)method
                      refreshRequest:(BOOL)refresh
                               cache:(BOOL)cache
                              params:(NSDictionary *)params
                       progressBlock:(LcbRequestProgress)progressBlock
                        successBlock:(LcbResponseSuccessBlock)successBlock
                           failBlock:(LcbResponseFailBlock)failBlock
{
    __block LcbURLSessionTask *session = nil;
    url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    AFHTTPSessionManager *manager = [self manager];
    if (networkStatus == NetworkStatusNotReachable) {
        if (failBlock) failBlock(Lcb_NET_ERROR);
        return session;
    }
    if (cache) {
        id responseObj = [[XDCacheManager shareManager] getCacheResponseObjectWithRequestUrl:url params:params];
        if (responseObj) {
            if (successBlock) successBlock(responseObj);
        }
    }
    
    switch (method) {
        case NRequestMethod_GET:
        {
            session = [manager GET:url
                        parameters:params
                       // progress:^(NSProgress * _Nonnull downloadProgress) {
                       //    if (progressBlock) progressBlock(downloadProgress.completedUnitCount,
                       //                                     downloadProgress.totalUnitCount);
                       
                       //}
                           success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                               if (successBlock) successBlock(responseObject);
                               
                               if (cache) [[XDCacheManager shareManager] cacheResponseObject:responseObject requestUrl:url params:params];
                               
                               [[self allTasks] removeObject:session];
                               
                           } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                               if (failBlock) failBlock(error);
                               [[self allTasks] removeObject:session];
                               
                           }];
        }
            break;
        case NRequestMethod_POST:
        {
            session = [manager POST:url
                         parameters:params
                       // progress:^(NSProgress * _Nonnull uploadProgress) {
                       //     if (progressBlock) progressBlock(uploadProgress.completedUnitCount,
                       //                                      uploadProgress.totalUnitCount);
                       
                       // }
                            success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                                if (successBlock) successBlock(responseObject);
                                
                                if (cache) [[XDCacheManager shareManager] cacheResponseObject:responseObject requestUrl:url params:params];
                                
                                if ([[self allTasks] containsObject:session]) {
                                    [[self allTasks] removeObject:session];
                                }
                                
                            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                                if (failBlock) failBlock(error);
                                [[self allTasks] removeObject:session];
                                
                            }];
        }
            break;
        case NRequestMethod_PUT:
        {
            session = [manager PUT:url
                       parameters:params
                       // progress:^(NSProgress * _Nonnull downloadProgress) {
                       //    if (progressBlock) progressBlock(downloadProgress.completedUnitCount,
                       //                                     downloadProgress.totalUnitCount);
                       
                       //}
                           success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                               if (successBlock) successBlock(responseObject);
                               
                               if (cache) [[XDCacheManager shareManager] cacheResponseObject:responseObject requestUrl:url params:params];
                               
                               [[self allTasks] removeObject:session];
                               
                           } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                               if (failBlock) failBlock(error);
                               [[self allTasks] removeObject:session];
                               
                           }];
        }
            break;
        case NRequestMethod_DELETE:
        {
            session = [manager DELETE:url
                        parameters:params
                       // progress:^(NSProgress * _Nonnull downloadProgress) {
                       //    if (progressBlock) progressBlock(downloadProgress.completedUnitCount,
                       //                                     downloadProgress.totalUnitCount);
                       
                       //}
                           success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                               if (successBlock) successBlock(responseObject);
                               
                               if (cache) [[XDCacheManager shareManager] cacheResponseObject:responseObject requestUrl:url params:params];
                               
                               [[self allTasks] removeObject:session];
                               
                           } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                               if (failBlock) failBlock(error);
                               [[self allTasks] removeObject:session];
                               
                           }];
        }
            break;
        default:
            break;
    }
    
    
    
    if ([self haveSameRequestInTasksPool:session] && !refresh) {
        [session cancel];
        return session;
    }else {
        LcbURLSessionTask *oldTask = [self cancleSameRequestInTasksPool:session];
        if (oldTask) [[self allTasks] removeObject:oldTask];
        if (session) [[self allTasks] addObject:session];
        [session resume];
        return session;
    }
    
}

#pragma mark - 文件上传
+ (LcbURLSessionTask *)uploadFileWithUrl:(NSString *)url
                                 params:(NSDictionary *)params
                               fileData:(NSData *)data
                                   type:(NSString *)type
                                   name:(NSString *)name
                               fileName:(NSString *)filename
                               mimeType:(NSString *)mimeType
                          progressBlock:(LcbUploadProgressBlock)progressBlock
                           successBlock:(LcbResponseSuccessBlock)successBlock
                              failBlock:(LcbResponseFailBlock)failBlock {
    __block LcbURLSessionTask *session = nil;
    
    AFHTTPSessionManager *manager = [self manager];
    if (networkStatus == NetworkStatusNotReachable) {
        if (failBlock) failBlock(Lcb_NET_ERROR);
        return session;
    }
    
    session = [manager POST:url
                 parameters:params
  constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
      NSString *fileWithSuffix = [NSString stringWithFormat:@"%@.%@",filename,type];
      
      [formData appendPartWithFileData:data name:name fileName:fileWithSuffix mimeType:mimeType];
        
    }
                   //progress:^(NSProgress * _Nonnull uploadProgress) {
       // if (progressBlock) progressBlock (uploadProgress.completedUnitCount,uploadProgress.totalUnitCount);
        
    //}
    success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (successBlock) successBlock(responseObject);
        [[self allTasks] removeObject:session];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failBlock) failBlock(error);
        [[self allTasks] removeObject:session];
        
    }];
    
    [session resume];
    
    if (session) [[self allTasks] addObject:session];
    
    return session;
}
+ (LcbURLSessionTask *)uploadFileWithUrl:(NSString *)url
                                 params:(NSDictionary *)params
                               fileDatas:(NSArray *)datas
                          progressBlock:(LcbUploadProgressBlock)progressBlock
                           successBlock:(LcbResponseSuccessBlock)successBlock
                              failBlock:(LcbResponseFailBlock)failBlock {
    __block LcbURLSessionTask *session = nil;
    
    AFHTTPSessionManager *manager = [self manager];
    if (networkStatus == NetworkStatusNotReachable) {
        if (failBlock) failBlock(Lcb_NET_ERROR);
        return session;
    }
    
    session = [manager POST:url
                 parameters:params
                  constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
                      
                      NSString *suf = @"jpeg";
                      if (suf == nil) {
                          suf = @"";
                      }
                      NSInteger count = datas.count;
                      for (int i = 0; i < count; i ++) {
                          NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
                          formatter.dateFormat = @"yyyyMMddHHmmss";
                          NSString *str = [formatter stringFromDate:[NSDate date]];
                          NSString *fileName = [NSString stringWithFormat:@"%@%d", str, i];
                          //根据mimetype截取文件后缀
                          NSString *fileWithSuffix = [fileName stringByAppendingPathExtension:suf];
                          NSString *name = @"image[]";
                          NSString *type = @"image/jpeg";
                          
                          // 上传图片，以文件流的格式
                          [formData appendPartWithFileData:datas[i] name:name fileName:fileWithSuffix mimeType:type];
                      }
                      
                  }
               //progress:^(NSProgress * _Nonnull uploadProgress) {
               // if (progressBlock) progressBlock (uploadProgress.completedUnitCount,uploadProgress.totalUnitCount);
               
               //}
                    success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                        if (successBlock) successBlock(responseObject);
                        [[self allTasks] removeObject:session];
                        
                    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                        if (failBlock) failBlock(error);
                        [[self allTasks] removeObject:session];
                        
                    }];
    
    [session resume];
    
    if (session) [[self allTasks] addObject:session];
    
    return session;
}

#pragma mark - 多文件上传
+ (NSArray *)uploadMultFileWithUrl:(NSString *)url
                            params:(NSDictionary *)params
                         fileDatas:(NSArray *)datas
                     progressBlock:(LcbUploadProgressBlock)progressBlock
                      successBlock:(LcbMultUploadSuccessBlock)successBlock
                         failBlock:(LcbMultUploadFailBlock)failBlock {
    
    if (networkStatus == NetworkStatusNotReachable) {
        if (failBlock) failBlock(@[Lcb_NET_ERROR]);
        return nil;
    }
    __block NSMutableArray *sessions = [NSMutableArray array];
    __block NSMutableArray *responses = [NSMutableArray array];
    __block NSMutableArray *failResponse = [NSMutableArray array];
    
    dispatch_group_t uploadGroup = dispatch_group_create();
    
    NSInteger count = datas.count;
    for (int i = 0; i < count; i++) {
        __block LcbURLSessionTask *session = nil;
       
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = @"yyyyMMddHHmmss";
        NSString *str = [formatter stringFromDate:[NSDate date]];
        NSString *fileName = [NSString stringWithFormat:@"%@%d", str, i];
        
        dispatch_group_enter(uploadGroup);
        
        session = [self uploadFileWithUrl:url
                                   params:params
                                 fileData:datas[i]
                                     type:@"jpeg"
                                     name:@"image[]"
                                 fileName:fileName
                                 mimeType: @"image/jpeg"
                            progressBlock:^(int64_t bytesWritten, int64_t totalBytes) {
                                if (progressBlock) progressBlock(bytesWritten,
                                                                 totalBytes);
                                
                            } successBlock:^(id response) {
                                [responses addObject:response];
                                
                                dispatch_group_leave(uploadGroup);
                                
                                [sessions removeObject:session];
                                
                            } failBlock:^(NSError *error) {
                                NSError *Error = [NSError errorWithDomain:url code:-999 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"第%d次上传失败",i]}];
                                
                                [failResponse addObject:Error];
                                
                                dispatch_group_leave(uploadGroup);
                                
                                [sessions removeObject:session];
                            }];
        
        [session resume];
        
        if (session) [sessions addObject:session];
    }
    
    [[self allTasks] addObjectsFromArray:sessions];
    
    dispatch_group_notify(uploadGroup, dispatch_get_main_queue(), ^{
        if (responses.count > 0) {
            if (successBlock) {
                successBlock([responses copy]);
                if (sessions.count > 0) {
                    [[self allTasks] removeObjectsInArray:sessions];
                }
            }
        }
        
        if (failResponse.count > 0) {
            if (failBlock) {
                failBlock([failResponse copy]);
                if (sessions.count > 0) {
                    [[self allTasks] removeObjectsInArray:sessions];
                }
            }
        }
        
    });
    
    return [sessions copy];
}

#pragma mark - 下载
+ (LcbURLSessionTask *)downloadWithUrl:(NSString *)url
                        progressBlock:(LcbDownloadProgress)progressBlock
                         successBlock:(LcbDownloadSuccessBlock)successBlock
                            failBlock:(LcbDownloadFailBlock)failBlock {
    NSString *type = nil;
    NSArray *subStringArr = nil;
    __block LcbURLSessionTask *session = nil;
    NSURL *fileUrl = [[XDCacheManager shareManager] getDownloadDataFromCacheWithRequestUrl:url];
    
    if (fileUrl) {
        if (successBlock) successBlock(fileUrl);
        return nil;
    }
    
    if (url) {
        subStringArr = [url componentsSeparatedByString:@"."];
        if (subStringArr.count > 0) {
            type = subStringArr[subStringArr.count - 1];
        }
    }
    
    AFHTTPSessionManager *manager = [self manager];
    //响应内容序列化为二进制
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    
    session = [manager GET:url
                parameters:nil
                 // progress:^(NSProgress * _Nonnull downloadProgress) {
                 //     if (progressBlock) progressBlock(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
                      
                 // }
                   success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                      if (successBlock) {
                          NSData *dataObj = (NSData *)responseObject;
                          
                          [[XDCacheManager shareManager] storeDownloadData:dataObj requestUrl:url];
                          
                          NSURL *downFileUrl = [[XDCacheManager shareManager] getDownloadDataFromCacheWithRequestUrl:url];
                          
                          successBlock(downFileUrl);
                      }
                      
                  } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                      if (failBlock) {
                          failBlock (error);
                      }
                  }];
    
    [session resume];
    
    if (session) [[self allTasks] addObject:session];
    
    return session;
    
}

#pragma mark - other method
+ (void)setupTimeout:(NSTimeInterval)timeout {
    requestTimeout = timeout;
}

+ (void)cancleAllRequest {
    @synchronized (self) {
        [[self allTasks] enumerateObjectsUsingBlock:^(LcbURLSessionTask  *_Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([obj isKindOfClass:[LcbURLSessionTask class]]) {
                [obj cancel];
            }
        }];
        [[self allTasks] removeAllObjects];
    }
}

+ (void)cancelRequestWithURL:(NSString *)url {
    if (!url) return;
    @synchronized (self) {
        [[self allTasks] enumerateObjectsUsingBlock:^(LcbURLSessionTask * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([obj isKindOfClass:[LcbURLSessionTask class]]) {
                if ([obj.currentRequest.URL.absoluteString hasSuffix:url]) {
                    [obj cancel];
                    *stop = YES;
                }
            }
        }];
    }
}

+ (void)configHttpHeader:(NSDictionary *)httpHeader {
    headers = httpHeader;
}

+ (NSArray *)currentRunningTasks {
    return [[self allTasks] copy];
}

@end


@implementation LcbNetworking (cache)

+ (NSUInteger)totalCacheSize {
    return [[XDCacheManager shareManager] totalCacheSize];
}

+ (NSUInteger)totalDownloadDataSize {
    return [[XDCacheManager shareManager] totalDownloadDataSize];
}

+ (void)clearDownloadData {
    [[XDCacheManager shareManager] clearDownloadData];
}

+ (NSString *)getDownDirectoryPath {
    return [[XDCacheManager shareManager] getDownDirectoryPath];
}

+ (NSString *)getCacheDiretoryPath {
    return [[XDCacheManager shareManager] getCacheDiretoryPath];
}

+ (void)clearTotalCache {
    [[XDCacheManager shareManager] clearTotalCache];
}

@end
